# -*- coding: binary -*-
require 'msf/core'

module Msf

###
#
# This module exposes methods for accessing NDMP services
#
###
module Exploit::Remote::NDMP

  include Exploit::Remote::Tcp

  #
  # Creates an instance of a NDMP exploit module.
  #
  def initialize(info = {})
    super

    # Register the options that all NDMP exploits may make use of.
    register_options(
      [
        Opt::RHOST,
        Opt::RPORT(10000),
      ], Msf::Exploit::Remote::NDMP)

    self.recv_buff = ''
  end

  #
  # Flush the receive buffer on a new connection
  #
  def connect
    super
    self.recv_buff = ''
  end

  #
  # This method dumps ndmp version information
  #
  def ndmp_info
    connect
    req = [
        1,               # Sequence number
        Time.now.to_i,   # Current time
        0,               # Message type (request)
        0x108,           # Message name (version)
        0,               # Reply sequence number
        0,               # Error status
      ].pack('NNNNNN')

    resp = ndmp_recv()
    ndmp_send(req)
    resp = ndmp_recv()
    disconnect

    if !(resp and resp.length > 28)
      return false
    end

    info = { }

    i = 32

    vend_len = resp[i, 4].unpack('N')[0]
    vend     = resp[i + 4, vend_len]
    i += vend_len + 4 + 1

    prod_len = resp[i, 4].unpack('N')[0]
    prod     = resp[i + 4, prod_len]
    i += prod_len + 4 + 1

    vers_len = resp[i, 4].unpack('N')[0]
    vers     = resp[i + 4, vers_len]
    i += vers_len + 4 + 1

    info['Version'] = vers
    info['Product'] = prod
    info['Vendor']  = vend

    return info
  end

  #
  # This method reads from the socket and parses out a single
  # NDMP response, buffering the rest
  #
  def ndmp_recv(nsock = self.sock)
    # Attempt to read at least four bytes (the length value)
    if (self.recv_buff.length < 4)
      self.recv_buff << ( sock.get_once( 4 - self.recv_buff.length, 5) || '' )
    end

    # If we did not receive a full length value, return early
    if (self.recv_buff.length < 4)
      return false
    end

    # Read the length header out of the message
    dlen = self.recv_buff[0, 4].unpack('N')[0] & 0x7fffffff

    # Read any pending data and append it to the buffer
    self.recv_buff << ( sock.get_once || '' )

    # Do we have the entire response message?
    if (self.recv_buff.length >= dlen + 4)
      return self.recv_buff.slice!(0, dlen + 4)
    end

    return false
  end

  #
  # This method tacks a length header on a packet then sends
  # it out the socket
  #
  def ndmp_send(data, nsock = self.sock)
    nsock.put( [ data.length + 0x80000000 ].pack('N') + data )
  end

  attr_accessor	:recv_buff
end
end

